package com.strangebreeds.therefromhere;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.xerces.dom.DocumentImpl;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class XMLCreator
{
	private Logger			logger			= Logger
													.getLogger(XMLCreator.class);
	private DocumentImpl	eventTextDOM	= null;

	{
		logger.setLevel(Level.DEBUG);
	}

	public XMLCreator()
	{
		try
		{
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
			xml += "<tfh datetime=\"" + df.format(new Date()) + "\" " + ">"
					+ "<message/>" + "</tfh>";
			eventTextDOM = (DocumentImpl) this.parseDOM(new InputSource(
					new ByteArrayInputStream(xml.getBytes())));
		}
		catch (IOException e)
		{
			logger.error(
					"An IOException was thrown when trying to create the audit xml: "
							+ e.getMessage(), e);
		}
		catch (SAXException saxe)
		{
			logger.error(
					"A SAXException was thrown when trying to create the audit xml: "
							+ saxe.getMessage(), saxe);
		}
	}

	private Document parseDOM(InputSource xml) throws IOException, SAXException
	{
		DOMParser parser = new DOMParser();
		Document dom = null;
		parser.parse(xml);
		dom = parser.getDocument();
		return dom;
	}

	public String getDocAsXMLString(boolean prettyPrint)
	{
		String eventMessage = new String();

		try
		{
			TransformerFactory factory = TransformerFactory.newInstance();
			if (prettyPrint)
			{
				try
				{
					factory.setAttribute("indent-number", 4);
				}
				catch (IllegalArgumentException iae)
				{
					logger.debug("Caught error trying to set the indent-number attribute: " + iae.getMessage(), iae);
				}
			}

			Transformer transformer = factory.newTransformer();
			if (prettyPrint)
			{
				transformer.setOutputProperty(OutputKeys.INDENT, "yes");
				transformer.setOutputProperty(OutputKeys.METHOD, "xml");
			}

			StreamResult result = new StreamResult(new StringWriter());
			DOMSource source = new DOMSource(eventTextDOM);
			transformer.transform(source, result);

			eventMessage = result.getWriter().toString();
		}
		catch (TransformerException te)
		{
			logger.error("While transforming the audit xml to a string, caught TransformerException: " + te.getMessage(), te);
			eventMessage = "Sorry, got TransformerException " + te.getMessage();
		}

		return eventMessage;
	}

	/**
	 * Add a node to the Audit XML. This adds the node to the message element.
	 * 
	 * @param node
	 */
	public void addNode(Node node)
	{
		eventTextDOM.getElementsByTagName("message").item(0).appendChild(node);
	}

	/**
	 * Creates an XML Element for this AuditRecord and returns it. This element
	 * is NOT yet a part of the XML document as you must use the addNode
	 * function or manually add the node through the getRootElelment function
	 * too add a new Element or node.
	 * 
	 * @param name
	 * @return Element
	 */
	public Element createXMLElement(String name)
	{
		return eventTextDOM.createElement(name);
	}

	/**
	 * Creates and returns a text node. You must append the text node to the
	 * node you create with createXMLElement using it's append node function.
	 * 
	 * @param text
	 * @return
	 */
	public Node createXMLTextNode(String text)
	{
		return eventTextDOM.createTextNode(text);
	}

	/**
	 * Parses and XML string and attaches it as a node under the audit's
	 * 'message' element. This is the easiest way to add your XML data.
	 * 
	 * @param nodeAsXMLString
	 * @throws IOException
	 * @throws SAXException
	 */
	public void addNode(String nodeAsXMLString) throws IOException, SAXException
	{
		Document doc = this.parseDOM(new InputSource(new ByteArrayInputStream(nodeAsXMLString.getBytes())));
		Node newNode = eventTextDOM.importNode(doc.getDocumentElement(), true);
		eventTextDOM.getElementsByTagName("message").item(0).appendChild(newNode);
	}
}
